-
Notifications
You must be signed in to change notification settings - Fork 1.1k
i22587 Divergence check for Match Types #24661
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
| case AnyVal => Wrap[List[Int]] | ||
|
|
||
| @main def test03(): Unit = | ||
| val e1: Wrap[Int] = ??? // error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
List[Int] is not AnyVal , so then it stops there right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're absolutely right. I've fixed the test case.
The divergence check currently applies even to match types that do not strictly reduce (stuck matches). Instead of simply stopping and returning NoType (or the stuck type), the algorithm detects the increasing size of the scrutinee and correctly returns an ErrorType.
Thanks for the catch! I would have missed that otherwise.
7de6009 to
3423338
Compare
|
Some failures might be due to #24730. |
|
25 failings tests: They seem mostly related to tuples. |
5f55a9a to
98b1d4e
Compare
Previously, `typeSize` reported different values for standard `TupleN` types (e.g., `(A, B)`) compared to their equivalent recursive pair encodings (e.g., `A *: B *: EmptyTuple`). This discrepancy occurred because `TupleN` is a flat `AppliedType`, while the nested encoding forms a deeper tree structure. This patch modifies `TypeSizeAccumulator` to canonicalize `TupleN` types into their recursive `*:` representation before calculating the size. This ensures that the size metric is consistent regardless of whether the tuple is represented syntactically or structurally. This change is verified by a new unit test in `TypesTest`, which confirms that both `Tuple3[Int, Boolean, Double]` and its recursive equivalent `Int *: Boolean *: Double *: EmptyTuple` now yield identical `typeSize` values. Fixes scala#24730
This is the first step towards implementing the full divergence check algorithm for Match Types. The logic enforces that recursive reduction steps must have a strictly smaller size than the sum of the type sizes of the scrutinee. This check applies to match types during reduction as well as those that do not reduce. When the algorithm detects a non-decreasing size, it returns an ErrorType to prevent infinite recursion. This work is part of the research project specified by Martin Odersky in issue scala#22587.
This commit relaxes the previous strict size-decreasing constraint for
Match Type reduction, allowing for more expressive recursive types while
maintaining termination guarantees.
Key changes:
1. Lexicographical Comparison: Instead of comparing the sum of argument
sizes, the algorithm now compares the list of argument sizes
lexicographically. This provides a more precise metric for progress.
2. Bounded Variation: Reductions are no longer required to strictly
decrease in size at every step. Same-size reductions are now allowed,
provided the sequence of arguments has not been seen before in the
current reduction chain (cycle detection).
3. History Stack: A stack of previously seen argument lists is maintained
for the current size baseline.
- If size strictly decreases: The stack is cleared (progress made).
- If size increases: An ErrorType is returned (divergence).
- If size is equal: The arguments are checked against the stack.
Repeating arguments trigger a cycle error; new arguments are added
to the stack.
Since the number of type combinations is finite, disallowing growth
while preventing cycles guarantees the algorithm will eventually terminate.
98b1d4e to
05a86ae
Compare
Closes #22587: Implement Divergence Checking for Match Types
Note to Reviewers: I have rebased this branch on top of PR #24743 to resolve CI failures caused by the
typeSizebug. As a result, the commit from that PR currently appears in this branch's history. Once PR #24743 is accepted and merged into main, that extra commit will automatically be skipped or dropped from this PR during the final merge process.This pull request closes #22587 by implementing a robust divergence checking mechanism for Match Types. Currently, the compiler relies on catching
StackOverflowErrorto handle infinite recursion during match type reduction, which is an unsafe and inefficient approach. This PR replaces that behavior by adapting the "Open Implicits" divergence checking algorithm (from SIP-31) specifically forAppliedTypesandMatchTypes.To achieve this, I have introduced a new
Propertyin theContextto track the history of types currently being reduced. The algorithm enforces termination by verifying that the size of the match type arguments decreases according to a lexicographical ordering. In cases where the size remains constant across a reduction step, the algorithm utilizes the history memory to verify that the Match Type arguments have not been encountered before (detecting cycles).In this example, reducing
Unwrap[Option[Option[Int]]]is allowed because the argument shrinks fromOption[Option[Int]]toOption[Int].Here,
Increase[0,0,0]passes through all binary combinations. The algorithm permits this because, although the type size remains constant, no specific tuple combination is repeated in the history.And rejects divergence caused by loops (same size, same state):